﻿#region Copyright (c) 2011, thinktecture (http://www.thinktecture.com)

/*
   thinktecture.UIAnnotations was developed by Jörg Neumann.
   Copyright (c) 2011, thinktecture (http://www.thinktecture.com).
   All rights reserved, comes as-is and without any warranty. Use of this
   source file is governed by the license which is contained in LICENSE.TXT 
   in the distribution.
*/

#endregion

using System;
using System.Linq;
using System.Windows;
using System.Windows.Data;
using System.Windows.Markup;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.ComponentModel;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using Thinktecture.DataForm.Controls;
using System.Reflection;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace Thinktecture.DataForm
{
    [TemplateVisualState(GroupName = "CommonStates", Name = "Normal")]
    [TemplateVisualState(GroupName = "CommonStates", Name = "Disabled")]
    [TemplatePart(Name = "PART_Items", Type = typeof(Grid))]
    [DefaultProperty("DataSource")]
    [DefaultBindingProperty("DataSource")]
    [ContentProperty("DataFormItems")]
    public partial class DataForm : ContentControl, ICollectionView
    {

        #region Private Variables

        private Type _dataObjectType;
        private ListCollectionView _source;
        private DataTypeDescriptor _typeDescriptor;
        private DataFormRowCollection _rowCollection;
        private Dictionary<FrameworkElement, BindingExpression> _bindingExpressions;
        private Grid _detailsGrid;
        private bool _focusHandlerRegistered;

        #endregion

        #region Constructors

        static DataForm()
        {
            DefaultStyleKeyProperty.OverrideMetadata(
                typeof(DataForm),
                new FrameworkPropertyMetadata(typeof(DataForm)));
        }

        public DataForm()
        {
            this.DataFormItems = new ObservableCollection<DataFormItem>();
            this.LayoutUpdated += this.OnLayoutUpdated;
            this.MoveFirstCommand = new DelegateCommand(this.CanExecuteMoveFirstCommand, this.ExecuteMoveFirstCommand);
            this.MovePreviousCommand = new DelegateCommand(this.CanExecuteMovePreviousCommand, this.ExecuteMovePreviousCommand);
            this.MoveNextCommand = new DelegateCommand(this.CanExecuteMoveNextCommand, this.ExecuteMoveNextCommand);
            this.MoveLastCommand = new DelegateCommand(this.CanExecuteMoveLastCommand, this.ExecuteMoveLastCommand);
            this.MoveToPositionCommand = new DelegateCommand(this.CanExecuteMoveToPositionCommand, this.ExecuteMoveToPositionCommand);
            this.MoveToItemCommand = new DelegateCommand(this.CanExecuteMoveToItemCommand, this.ExecuteMoveToItemCommand);
            this.AddCommand = new DelegateCommand(this.CanExecuteAddCommand, this.ExecuteAddCommand);
            this.RemoveCommand = new DelegateCommand(this.CanExecuteRemoveCommand, this.ExecuteRemoveCommand);
        }

        #endregion

        #region Overrides

        #region OnApplyTemplate

        public override void OnApplyTemplate()
        {
            _detailsGrid = base.GetTemplateChild("PART_Items") as Grid;
            if (_detailsGrid == null)
                throw new InvalidOperationException("Can't find \"PART_Items\" in control template!");
            base.OnApplyTemplate();
        }

        #endregion

        #endregion

        #region Properties

        #region AutoGenerateColumns

        public static readonly DependencyProperty AutoGenerateColumnsProperty =
            DependencyProperty.Register("AutoGenerateColumns", typeof(bool), typeof(DataForm),
            new PropertyMetadata(true, OnAutoGenerateColumnsPropertyChanged));
        public bool AutoGenerateColumns { get { return (bool)GetValue(AutoGenerateColumnsProperty); } set { SetValue(AutoGenerateColumnsProperty, value); } }
        
        private static void OnAutoGenerateColumnsPropertyChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            ((DataForm)sender).OnAutoGenerateColumnsPropertyChangedCore(sender, e);
        }
        
        private void OnAutoGenerateColumnsPropertyChangedCore(object sender, DependencyPropertyChangedEventArgs e)
        {
            //Generate();
        }

        #endregion

        #region DataFormItems

        public static readonly DependencyProperty DataFormItemsProperty =
            DependencyProperty.Register("DataFormItems", typeof(ObservableCollection<DataFormItem>), typeof(DataFormItem),
            new PropertyMetadata(null, OnDataFormItemsPropertyChanged));

        public ObservableCollection<DataFormItem> DataFormItems
        {
            get { return (ObservableCollection<DataFormItem>)GetValue(DataFormItemsProperty); }
            set { SetValue(DataFormItemsProperty, value); }
        }

        private static void OnDataFormItemsPropertyChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            ((DataForm)sender).OnDataFormItemsPropertyChangedCore(sender, e);
        }

        private void OnDataFormItemsPropertyChangedCore(object sender, DependencyPropertyChangedEventArgs e)
        {
            //this.Generate();
        }

        #endregion

        #region DataSource

        public static readonly DependencyProperty DataSourceProperty =
            DependencyProperty.Register("DataSource", typeof(object), typeof(DataForm),
            new PropertyMetadata(null, OnDataSourcePropertyChanged, OnCoerceDataSource));
        public object DataSource { get { return GetValue(DataSourceProperty); } set { SetValue(DataSourceProperty, value); } }

        private static void OnDataSourcePropertyChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            ((DataForm)sender).OnDataSourcePropertyChangedCore(sender, e);
        }

        private void OnDataSourcePropertyChangedCore(object sender, DependencyPropertyChangedEventArgs e)
        {
            this.Generate();
            this.RefreshCommands();
        }

        private static object OnCoerceDataSource(DependencyObject d, object baseValue)
        {
            return ((DataForm)d).OnCoerceDataSourceCore(d, baseValue);
        }

        private object OnCoerceDataSourceCore(DependencyObject d, object baseValue)
        {
            this.UnregisterChangeNotification();
            return baseValue;
        }

        #endregion

        #region RowHeight

        public static readonly DependencyProperty RowSpacingProperty =
            DependencyProperty.Register("RowSpacing", typeof(double), typeof(DataForm),
            new FrameworkPropertyMetadata(4d, FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender, OnRowSpacingPropertyChanged));
        public double RowSpacing { get { return (double)GetValue(RowSpacingProperty); } set { SetValue(RowSpacingProperty, value); } }
        private static void OnRowSpacingPropertyChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            //((DataForm)sender).GenerateForm();
        }

        #endregion

        #region ColumnSpacing

        public static readonly DependencyProperty ColumnSpacingProperty =
            DependencyProperty.Register("ColumnSpacing", typeof(double), typeof(DataForm),
            new FrameworkPropertyMetadata(5d, FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender, OnColumnSpacingPropertyChanged));
        public double ColumnSpacing { get { return (double)GetValue(ColumnSpacingProperty); } set { SetValue(ColumnSpacingProperty, value); } }
        private static void OnColumnSpacingPropertyChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            //((DataForm)sender).GenerateForm();
        }

        #endregion

        #region CornerRadius
        
        public static readonly DependencyProperty CornerRadiusProperty =
            DependencyProperty.Register("CornerRadius", typeof(CornerRadius), typeof(DataForm),
            new PropertyMetadata(new CornerRadius(0d), OnCornerRadiusPropertyChanged));
        public CornerRadius CornerRadius { get { return (CornerRadius)GetValue(CornerRadiusProperty); } set { SetValue(CornerRadiusProperty, value); } }
        private static void OnCornerRadiusPropertyChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            //((DataForm)sender).GenerateForm();
        }

        #endregion

        #region Error

        public static readonly DependencyProperty ErrorProperty =
            DependencyProperty.Register("Error", typeof(string), typeof(DataForm));
        public string Error { get { return (string)GetValue(ErrorProperty); } set { SetValue(ErrorProperty, value); } }

        #endregion

        #region HasError

        public static readonly DependencyProperty HasErrorProperty =
            DependencyProperty.Register("HasError", typeof(bool), typeof(DataForm), new PropertyMetadata(false));
        public bool HasError { get { return (bool)GetValue(HasErrorProperty); } set { SetValue(HasErrorProperty, value); } }

        #endregion

        #region ControlMappings

        public static readonly DependencyProperty ControlMappingsProperty =
            DependencyProperty.Register("ControlMappings", typeof(ControlMappingCollection), typeof(DataForm), new UIPropertyMetadata(new ControlMappingCollection(), OnControlMappingsPropertyChanged));
        public ControlMappingCollection ControlMappings { get { return (ControlMappingCollection)GetValue(ControlMappingsProperty); } set { SetValue(ControlMappingsProperty, value); } }

        private static void OnControlMappingsPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            ((DataForm)sender).OnControlMappingsChanged(sender, e);
        }

        private void OnControlMappingsChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
        }

        #endregion
        
        #region SubDataSources

        public static readonly DependencyProperty SubDataSourcesProperty =
            DependencyProperty.Register("SubDataSources", typeof(DataFormSubSourceCollection), typeof(DataForm), new UIPropertyMetadata(new DataFormSubSourceCollection(), OnSubDataSourcesChanged));
        public DataFormSubSourceCollection SubDataSources { get { return (DataFormSubSourceCollection)GetValue(SubDataSourcesProperty); } set { SetValue(SubDataSourcesProperty, value); } }

        private static void OnSubDataSourcesChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            ((DataForm)sender).OnSubDataSourcesChangedCore(sender, e);
        }

        private void OnSubDataSourcesChangedCore(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            //this.Generate();
        }
        #endregion

        #region Command Properties

        #region MoveFirstCommand

        public static readonly DependencyProperty MoveFirstCommandProperty =
            DependencyProperty.Register("MoveFirstCommand", typeof(DelegateCommand), typeof(DataForm));
        public DelegateCommand MoveFirstCommand { get { return (DelegateCommand)GetValue(MoveFirstCommandProperty); } set { SetValue(MoveFirstCommandProperty, value); } }

        #endregion

        #region MovePreviousCommand

        public static readonly DependencyProperty MovePreviousCommandProperty =
            DependencyProperty.Register("MovePreviousCommand", typeof(DelegateCommand), typeof(DataForm));
        public DelegateCommand MovePreviousCommand { get { return (DelegateCommand)GetValue(MovePreviousCommandProperty); } set { SetValue(MovePreviousCommandProperty, value); } }

        #endregion

        #region MoveNextCommand

        public static readonly DependencyProperty MoveNextCommandProperty =
            DependencyProperty.Register("MoveNextCommand", typeof(DelegateCommand), typeof(DataForm));
        public DelegateCommand MoveNextCommand { get { return (DelegateCommand)GetValue(MoveNextCommandProperty); } set { SetValue(MoveNextCommandProperty, value); } }

        #endregion

        #region MoveLastCommand

        public static readonly DependencyProperty MoveLastCommandProperty =
            DependencyProperty.Register("MoveLastCommand", typeof(DelegateCommand), typeof(DataForm));
        public DelegateCommand MoveLastCommand { get { return (DelegateCommand)GetValue(MoveLastCommandProperty); } set { SetValue(MoveLastCommandProperty, value); } }

        #endregion

        #region MoveToPositionCommand

        public static readonly DependencyProperty MoveToPositionCommandProperty =
            DependencyProperty.Register("MoveToPositionCommand", typeof(DelegateCommand), typeof(DataForm));
        public DelegateCommand MoveToPositionCommand { get { return (DelegateCommand)GetValue(MoveToPositionCommandProperty); } set { SetValue(MoveToPositionCommandProperty, value); } }

        #endregion

        #region MoveToItemCommand

        public static readonly DependencyProperty MoveToItemCommandProperty =
            DependencyProperty.Register("MoveToItemCommand", typeof(DelegateCommand), typeof(DataForm));
        public DelegateCommand MoveToItemCommand { get { return (DelegateCommand)GetValue(MoveToItemCommandProperty); } set { SetValue(MoveToItemCommandProperty, value); } }

        #endregion

        #region AddCommand

        public static readonly DependencyProperty AddCommandProperty =
            DependencyProperty.Register("AddCommand", typeof(DelegateCommand), typeof(DataForm));
        public DelegateCommand AddCommand { get { return (DelegateCommand)GetValue(AddCommandProperty); } set { SetValue(AddCommandProperty, value); } }

        #endregion

        #region RemoveCommand

        public static readonly DependencyProperty RemoveCommandProperty =
            DependencyProperty.Register("RemoveCommand", typeof(DelegateCommand), typeof(DataForm));
        public DelegateCommand RemoveCommand { get { return (DelegateCommand)GetValue(RemoveCommandProperty); } set { SetValue(RemoveCommandProperty, value); } }

        #endregion

        #region ActionCommand

        public static readonly DependencyProperty ActionCommandProperty =
            DependencyProperty.Register("ActionCommand", typeof(ActionCommand), typeof(DataForm), new UIPropertyMetadata(null, OnActionCommandPropertyChanged));
        public ActionCommand ActionCommand { get { return (ActionCommand)GetValue(ActionCommandProperty); } set { SetValue(ActionCommandProperty, value); } }

        private static void OnActionCommandPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            ((DataForm)sender).OnActionCommandChanged(sender, e);
        }

        private void OnActionCommandChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            var command = e.NewValue as ActionCommand;
            if (command != null)
            {
                command.SetExecutedHandler(this.CanExecuteActionCommand, this.ExecuteActionCommand);
            }
        }

        #endregion

        #region SelectionButtonCommand

        public static readonly DependencyProperty SelectionButtonCommandProperty =
            DependencyProperty.Register("SelectionButtonCommand", typeof(DelegateCommand), typeof(DataForm));
        public DelegateCommand SelectionButtonCommand { get { return (DelegateCommand)GetValue(SelectionButtonCommandProperty); } set { SetValue(SelectionButtonCommandProperty, value); } }

        #endregion

        #endregion

        #region ShowHeader

        public static readonly DependencyProperty ShowHeaderProperty =
            DependencyProperty.Register("ShowHeader", typeof(bool), typeof(DataForm), new UIPropertyMetadata(true));
        public bool ShowHeader { get { return (bool)GetValue(ShowHeaderProperty); } set { SetValue(ShowHeaderProperty, value); } }

        #endregion

        #region ShowFooter

        public static readonly DependencyProperty ShowFooterProperty =
            DependencyProperty.Register("ShowFooter", typeof(bool), typeof(DataForm), new UIPropertyMetadata(true));
        public bool ShowFooter { get { return (bool)GetValue(ShowFooterProperty); } set { SetValue(ShowFooterProperty, value); } }

        #endregion

        #region AllowAddNew

        public static readonly DependencyProperty AllowAddNewProperty =
            DependencyProperty.Register("AllowAddNew", typeof(bool), typeof(DataForm), new UIPropertyMetadata(true));
        public bool AllowAddNew { get { return (bool)GetValue(AllowAddNewProperty); } set { SetValue(AllowAddNewProperty, value); } }

        #endregion

        #region AllowRemove

        public static readonly DependencyProperty AllowRemoveProperty =
            DependencyProperty.Register("AllowRemove", typeof(bool), typeof(DataForm), new UIPropertyMetadata(true));
        public bool AllowRemove { get { return (bool)GetValue(AllowRemoveProperty); } set { SetValue(AllowRemoveProperty, value); } }

        #endregion

        #region IsReadOnly

        public static readonly DependencyProperty IsReadOnlyProperty =
            DependencyProperty.Register("IsReadOnly", typeof(bool), typeof(DataForm), new UIPropertyMetadata(false));
        public bool IsReadOnly { get { return (bool)GetValue(IsReadOnlyProperty); } set { SetValue(IsReadOnlyProperty, value); } }

        #endregion

        #region HeaderTemplate

        public static readonly DependencyProperty HeaderTemplateProperty =
            DependencyProperty.Register("HeaderTemplate", typeof(ControlTemplate), typeof(DataForm));
        public ControlTemplate HeaderTemplate { get { return (ControlTemplate)GetValue(HeaderTemplateProperty); } set { SetValue(HeaderTemplateProperty, value); } }

        #endregion

        #region FooterTemplate

        public static readonly DependencyProperty FooterTemplateProperty =
            DependencyProperty.Register("FooterTemplate", typeof(ControlTemplate), typeof(DataForm));
        public ControlTemplate FooterTemplate { get { return (ControlTemplate)GetValue(FooterTemplateProperty); } set { SetValue(FooterTemplateProperty, value); } }

        #endregion

        #region ShowEditTracker

        public static readonly DependencyProperty ShowEditTrackerProperty =
            DependencyProperty.Register("ShowEditTracker", typeof(bool), typeof(DataForm), new UIPropertyMetadata(true));
        public bool ShowEditTracker { get { return (bool)GetValue(ShowEditTrackerProperty); } set { SetValue(ShowEditTrackerProperty, value); } }

        #endregion

        #region ShowRowMarker

        public static readonly DependencyProperty ShowRowMarkerProperty =
            DependencyProperty.Register("ShowRowMarker", typeof(bool), typeof(DataForm), new UIPropertyMetadata(true));
        public bool ShowRowMarker { get { return (bool)GetValue(ShowRowMarkerProperty); } set { SetValue(ShowRowMarkerProperty, value); } }

        #endregion

        #region ShowRequiredFieldMarker

        public static readonly DependencyProperty ShowRequiredFieldMarkerProperty =
            DependencyProperty.Register("ShowRequiredFieldMarker", typeof(bool), typeof(DataForm), new UIPropertyMetadata(true));
        public bool ShowRequiredFieldMarker { get { return (bool)GetValue(ShowRequiredFieldMarkerProperty); } set { SetValue(ShowRequiredFieldMarkerProperty, value); } }

        #endregion

        #region ShowErrorMarker

        public static readonly DependencyProperty ShowErrorMarkerProperty =
            DependencyProperty.Register("ShowErrorMarker", typeof(bool), typeof(DataForm), new UIPropertyMetadata(true));
        public bool ShowErrorMarker { get { return (bool)GetValue(ShowErrorMarkerProperty); } set { SetValue(ShowErrorMarkerProperty, value); } }

        #endregion

        #region IsBusy

        public static readonly DependencyProperty IsBusyProperty =
            DependencyProperty.Register("IsBusy", typeof(bool), typeof(DataForm), new UIPropertyMetadata(false, OnIsBusyPropertyChanged));
        public bool IsBusy { get { return (bool)GetValue(IsBusyProperty); } set { SetValue(IsBusyProperty, value); } }

        private static void OnIsBusyPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            ((DataForm)sender).OnIsBusyChanged(sender, e);
        }

        private void OnIsBusyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            if ((bool)e.NewValue)
                VisualStateManager.GoToState(this, "Disabled", true);
            else
                VisualStateManager.GoToState(this, "Normal", true);
        }

        #endregion

        #region Title

        public static readonly DependencyProperty TitleProperty =
            DependencyProperty.Register("Title", typeof(object), typeof(DataForm));
        public string Title { get { return (string)GetValue(TitleProperty); } set { SetValue(TitleProperty, value); } }

        #endregion

        #region DefaultStatusText

        public static readonly DependencyProperty DefaultStatusTextProperty =
            DependencyProperty.Register("DefaultStatusText", typeof(string), typeof(DataForm), new UIPropertyMetadata(""));
        public string DefaultStatusText { get { return (string)GetValue(DefaultStatusTextProperty); } set { SetValue(DefaultStatusTextProperty, value); } }

        #endregion

        #region ResourceType

        public static readonly DependencyProperty ResourceTypeProperty =
            DependencyProperty.Register("ResourceType", typeof(Type), typeof(DataForm));
        public Type ResourceType { get { return (Type)GetValue(ResourceTypeProperty); } set { SetValue(ResourceTypeProperty, value); } }

        #endregion

        #region SelectionButtonsText

        public static readonly DependencyProperty SelectionButtonsTextProperty =
            DependencyProperty.Register("SelectionButtonsText", typeof(string), typeof(DataForm), new UIPropertyMetadata("..."));
        public string SelectionButtonsText { get { return (string)GetValue(SelectionButtonsTextProperty); } set { SetValue(SelectionButtonsTextProperty, value); } }

        #endregion

        #region DefaultImage

        public static readonly DependencyProperty DefaultImageProperty =
            DependencyProperty.Register("DefaultImage", typeof(ImageSource), typeof(DataForm));
        public ImageSource DefaultImage { get { return (ImageSource)GetValue(DefaultImageProperty); } set { SetValue(DefaultImageProperty, value); } }

        #endregion

        #region ShowDropShadow

        public static readonly DependencyProperty ShowDropShadowProperty =
            DependencyProperty.Register("ShowDropShadow", typeof(bool), typeof(DataForm), new UIPropertyMetadata(true, OnShowDropShadowPropertyChanged));
        public bool ShowDropShadow { get { return (bool)GetValue(ShowDropShadowProperty); } set { SetValue(ShowDropShadowProperty, value); } }

        private static void OnShowDropShadowPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            ((DataForm)sender).OnShowDropShadowChanged(sender, e);
        }

        private void OnShowDropShadowChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            if ((bool)e.NewValue)
            {
                var effect = this.TryFindResource("DataFormDropShadow") as System.Windows.Media.Effects.DropShadowEffect;
                this.Effect = effect;
            }
            else
            {
                this.Effect = null;
            }
        }

        #endregion

        #region HeaderStyle

        public static readonly DependencyProperty HeaderStyleProperty =
            DependencyProperty.Register("HeaderStyle", typeof(Style), typeof(DataForm));
        public Style HeaderStyle { get { return (Style)GetValue(HeaderStyleProperty); } set { SetValue(HeaderStyleProperty, value); } }

        #endregion

        #region FooterStyle

        public static readonly DependencyProperty FooterStyleProperty =
            DependencyProperty.Register("FooterStyle", typeof(Style), typeof(DataForm));
        public Style FooterStyle { get { return (Style)GetValue(FooterStyleProperty); } set { SetValue(FooterStyleProperty, value); } }

        #endregion

        #region CurrentStatusText

        public static readonly DependencyProperty CurrentStatusTextProperty =
            DependencyProperty.Register("CurrentStatusText", typeof(string), typeof(DataForm), new PropertyMetadata("Ready."));
        public string CurrentStatusText { get { return (string)GetValue(CurrentStatusTextProperty); } set { SetValue(CurrentStatusTextProperty, value); } }

        #endregion

        #region BusyIndicatorType

        public static readonly DependencyProperty BusyIndicatorTypeProperty =
            DependencyProperty.Register("BusyIndicatorType", typeof(IndicatorType), typeof(DataForm), new UIPropertyMetadata(IndicatorType.Wheel));
        public IndicatorType BusyIndicatorType { get { return (IndicatorType)GetValue(BusyIndicatorTypeProperty); } set { SetValue(BusyIndicatorTypeProperty, value); } }

        #endregion

        #region BusyIndicatorTemplate

        public static readonly DependencyProperty BusyIndicatorTemplateProperty =
            DependencyProperty.Register("BusyIndicatorTemplate", typeof(ControlTemplate), typeof(DataForm));
        public ControlTemplate BusyIndicatorTemplate { get { return (ControlTemplate)GetValue(BusyIndicatorTemplateProperty); } set { SetValue(BusyIndicatorTemplateProperty, value); } }

        #endregion

        #region ColumnCount

        public static readonly DependencyProperty ColumnCountProperty =
            DependencyProperty.Register("ColumnCount", typeof(int), typeof(DataForm), new UIPropertyMetadata(1, OnColumnCountPropertyChanged));
        public int ColumnCount { get { return (int)GetValue(ColumnCountProperty); } set { SetValue(ColumnCountProperty, value); } }

        private static void OnColumnCountPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            ((DataForm)sender).OnColumnCountChanged(sender, e);
        }

        private void OnColumnCountChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
        }

        #endregion

        #region MarkerStyle

        public static readonly DependencyProperty MarkerStyleProperty =
            DependencyProperty.Register("MarkerStyle", typeof(Style), typeof(DataForm));
        public Style MarkerStyle { get { return (Style)GetValue(MarkerStyleProperty); } set { SetValue(MarkerStyleProperty, value); } }

        #endregion
                
        #endregion

        #region Events

        #region ValidationErrorEvent

        public static readonly RoutedEvent ValidationErrorEvent =
            EventManager.RegisterRoutedEvent("ValidationError", RoutingStrategy.Bubble, typeof(DataFormValidationErrorEventHandler), typeof(DataForm));
        public event DataFormValidationErrorEventHandler ValidationError
        {
            add{ this.AddHandler(ValidationErrorEvent, value); }
            remove{ this.RemoveHandler(ValidationErrorEvent, value); }
        }
        protected virtual void OnValidationError(string errorMessage, string propertyName)
        {
            var args = new DataFormValidationErrorEventArgs(ValidationErrorEvent, this, errorMessage, propertyName);
            this.RaiseEvent(args);
        }

        #endregion

        #region ItemAddedEvent

        public static readonly RoutedEvent ItemAddedEvent =
            EventManager.RegisterRoutedEvent("ItemAdded", RoutingStrategy.Bubble, typeof(DataFormItemChangedEventHandler), typeof(DataForm));
        public event DataFormItemChangedEventHandler ItemAdded
        {
            add { this.AddHandler(ItemAddedEvent, value); }
            remove { this.RemoveHandler(ItemAddedEvent, value); }
        }
        protected virtual void OnItemAdded(object item)
        {
            var args = new DataFormItemChangedEventArgs(ItemAddedEvent, this, item);
            this.RaiseEvent(args);
        }

        #endregion

        #region ItemRemovedEvent

        public static readonly RoutedEvent ItemRemovedEvent =
            EventManager.RegisterRoutedEvent("ItemRemoved", RoutingStrategy.Bubble, typeof(DataFormItemChangedEventHandler), typeof(DataForm));
        public event DataFormItemChangedEventHandler ItemRemoved
        {
            add { this.AddHandler(ItemRemovedEvent, value); }
            remove { this.RemoveHandler(ItemRemovedEvent, value); }
        }
        protected virtual void OnItemRemoved(object item)
        {
            var args = new DataFormItemChangedEventArgs(ItemRemovedEvent, this, item);
            this.RaiseEvent(args);
        }

        #endregion
        
        #region ItemChangedEvent

        public static readonly RoutedEvent ItemChangedEvent =
            EventManager.RegisterRoutedEvent("ItemChanged", RoutingStrategy.Bubble, typeof(DataFormItemChangedEventHandler), typeof(DataForm));
        public event DataFormItemChangedEventHandler ItemChanged
        {
            add { this.AddHandler(ItemChangedEvent, value); }
            remove { this.RemoveHandler(ItemChangedEvent, value); }
        }
        protected virtual void OnItemChanged(object item)
        {
            var args = new DataFormItemChangedEventArgs(ItemChangedEvent, this, item);
            this.RaiseEvent(args);
        }

        #endregion

        #region SelectionButtonClickedEvent

        public static readonly RoutedEvent SelectionButtonClickedEvent =
    EventManager.RegisterRoutedEvent("SelectionButtonClicked", RoutingStrategy.Bubble, typeof(DataFormSelectionButtonClickedEventHandler), typeof(DataForm));
        public event DataFormSelectionButtonClickedEventHandler SelectionButtonClicked
        {
            add { this.AddHandler(SelectionButtonClickedEvent, value); }
            remove { this.RemoveHandler(SelectionButtonClickedEvent, value); }
        }
        protected virtual void OnSelectionButtonClicked(string propertyName, object item)
        {
            var args = new DataFormSelectionButtonClickedEventArgs(SelectionButtonClickedEvent, this, propertyName, item);
            this.RaiseEvent(args);
        }

        #endregion
        
        #endregion

        #region Private Functions

        #region Data Source Handling

        #region Generate

        private void Generate()
        {
            if (this.DataSource == null)
                return;

            // unregister change handler
            this.UnregisterChangeNotification();

            // create ListCollectionView from data source
            if (this.DataSource is ListCollectionView)
            {
                _source = this.DataSource as ListCollectionView;
            }
            else if (this.DataSource is CollectionViewSource)
            {
                var col = this.DataSource as CollectionViewSource;
                if (typeof(IList).IsAssignableFrom(col.Source.GetType()))
                {
                    this.CreateListCollectionView((IList)col.Source);
                }
                else
                {
                    throw new ArgumentException("The source of the given CollectionViewSource isn't an IList instance.");
                }
            }
            else if (this.DataSource is ICollectionView)
            {
                var col = this.DataSource as ICollectionView;
                if (typeof(IList).IsAssignableFrom(col.SourceCollection.GetType()))
                {
                    this.CreateListCollectionView((IList)col.SourceCollection);
                }
                else
                {
                    throw new ArgumentException("The source of the given ICollectionView isn't an IList instance.");
                }
            }
            else if (this.DataSource is IList)
            {
                this.CreateListCollectionView((IList)this.DataSource);
            }
            else if (this.DataSource is Type)
            {
                this.CreateListCollectionViewFromType((Type)this.DataSource);
            }
            else if (this.DataSource is IEnumerable)
            {
                throw new ArgumentException("The source property isn't an IList instance.");
            }
            else
            {
                this.CreateListCollectionViewFromType(this.DataSource.GetType(), this.DataSource);
            }
            
            if (_source != null)
            {
                _source.NewItemPlaceholderPosition = NewItemPlaceholderPosition.None;
                _source.MoveCurrentToFirst();
                if (_source.CurrentItem == null)
                {
                    _source.AddNew();
                }
                this.GetDataItemType();
                this.RegisterChangeNotification();
            }
            else
            {
                throw new InvalidOperationException("Can't read data source.");
            }
            if (this.IsLoaded)
            {
                this.AssignDataSource();
            }
            else
            {
                this.Loaded += this.OnLoaded;
            }
        }

        #endregion

        #region CreateListCollectionView

        private void CreateListCollectionView(IList list)
        {
            _source = new ListCollectionView(list);
        }

        #endregion

        #region CreateListCollectionViewFromType

        private IList CreateListCollectionViewFromType(Type itemType)
        {
            var listType = typeof(ObservableCollection<>).MakeGenericType(itemType);
            var list = Activator.CreateInstance(listType) as IList;
            this.CreateListCollectionView(list);
            return list;
        }

        private void CreateListCollectionViewFromType(Type itemType, object item)
        {
            var list = this.CreateListCollectionViewFromType(itemType);
            list.Add(item);
        }

        #endregion

        #region GetDataItemType

        private void GetDataItemType()
        {
            _dataObjectType = null;
            if (_source != null)
            {
                if (_source.MoveCurrentToFirst())
                {
                    _dataObjectType = _source.CurrentItem.GetType();
                }
                else if (_source.GetType().IsGenericType)
                {
                    var types = _source.GetType().GetGenericArguments();
                    if (types.Length > 0)
                    {
                        _dataObjectType = types[0];
                    }
                }
            }
        }

        #endregion

        #region AssignDataSource

        private void AssignDataSource()
        {
            if (_detailsGrid != null && _source != null)
                _detailsGrid.DataContext = _source.CurrentItem;
            this.GetDataItemType();
            this.GenerateForm();
        }

        #endregion
        
        #endregion
        
        #region Change Notification

        #region RegisterChangeNotification

        private void RegisterChangeNotification()
        {
            if (_source == null || _source.SourceCollection == null) return;
            
            // add list change handler
            var list = _source.SourceCollection;
            if (typeof(INotifyCollectionChanged).IsAssignableFrom(list.GetType()))
            {
                var change = list as INotifyCollectionChanged;
                change.CollectionChanged += this.OnDataCollectionChanged;
            }
            _source.CurrentChanging += this.OnCurrentChanging;
            _source.CurrentChanged += this.OnCurrentChanged;
            
            // add item list handler
            foreach (var item in _source)
            {
                var notifyItem = item as INotifyPropertyChanged;
                if (notifyItem != null)
                {
                    notifyItem.PropertyChanged += this.OnDataPropertyChanged;
                }
            }
        }

        #endregion

        #region UnregisterChangeNotification

        private void UnregisterChangeNotification()
        {
            if (_source == null || _source.SourceCollection == null) return;
            var list = _source.SourceCollection;
            if (typeof(INotifyCollectionChanged).IsAssignableFrom(list.GetType()))
            {
                var change = list as INotifyCollectionChanged;
                change.CollectionChanged -= this.OnDataCollectionChanged;
            }
            foreach (var item in _source)
            {
                var notifyItem = item as INotifyPropertyChanged;
                if (notifyItem != null)
                {
                    notifyItem.PropertyChanged -= this.OnDataPropertyChanged;
                }
            }
        }

        #endregion

        #endregion

        #region UI Generation

        #region GenerateForm

        private void GenerateForm()
        {
            _detailsGrid = base.GetTemplateChild("PART_Items") as Grid;
            if (_detailsGrid == null)
                return;
            
            int currentRow = 0;

            // unregister focus handler
            this.UnregisterFocusHandler();

            // reset UI
            _detailsGrid.Children.Clear();
            _detailsGrid.ColumnDefinitions.Clear();
            _rowCollection = new DataFormRowCollection();
            _bindingExpressions = new Dictionary<FrameworkElement, BindingExpression>();

            // Create user defined data form items
            if (this.DataFormItems != null && this.DataFormItems.Count > 0 && this.AutoGenerateColumns==false)
            {
                int rowCount = this.DataFormItems.Count;
                if (this.ColumnCount > rowCount)
                    this.ColumnCount = rowCount;
                int itemsPerColumn = (int)Math.Round((double)rowCount / this.ColumnCount, 0, MidpointRounding.AwayFromZero);
                Grid currentGrid = this.CreateColumnGrid(itemsPerColumn);
                for (int i = 0; i < this.ColumnCount; i++)
                {
                    _detailsGrid.ColumnDefinitions.Add(new ColumnDefinition { });
                    _detailsGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(20d, GridUnitType.Pixel) });
                }
                _detailsGrid.ColumnDefinitions.RemoveAt(_detailsGrid.ColumnDefinitions.Count - 1);
                int columnIndex = 0;
                int itemColumnCounter = 0;
                
                foreach (var item in this.DataFormItems)
                {
                    if (itemColumnCounter == itemsPerColumn)
                    {
                        currentRow = 0;
                        columnIndex++;
                        currentGrid = this.CreateColumnGrid(itemsPerColumn);
                        currentGrid.SetValue(Grid.ColumnProperty, columnIndex + 1);
                        itemColumnCounter = 0;
                    }
                    itemColumnCounter++;
                    
                    // create header
                    ContentPresenter header = new ContentPresenter();
                    if (item.Header is string)
                        header = new ContentPresenter() { Content = new Label { Content = item.Header.ToString() } };
                    else if (item.Header is TextBlock)
                        header = new ContentPresenter() { Content = new Label { Content = ((TextBlock)item.Header).Text } };
                    else
                        header = new ContentPresenter() { Content = item.Header };
                    header.Margin = new Thickness(0, 0, this.ColumnSpacing, 0);
                    header.VerticalAlignment = System.Windows.VerticalAlignment.Center;
                    header.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
                    header.SetValue(Grid.RowProperty, currentRow);
                    header.SetValue(Grid.ColumnProperty, 0);
                    currentGrid.Children.Add(header);

                    // create content
                    var content = new ContentPresenter() { Content = item.Content };
                    content.HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch;
                    content.VerticalAlignment = System.Windows.VerticalAlignment.Center;
                    content.SetValue(Grid.RowProperty, currentRow);
                    content.SetValue(Grid.ColumnProperty, 2);
                    this.AssignEmptyErrorTemplate((FrameworkElement)item.Content);
                    currentGrid.Children.Add(content);

                    // create property descriptor
                    var propDescriptor = AnnotationReader.FromElement((UIElement)item.Content, _source.CurrentItem);
                    
                    // create ui annotation markers
                    var leftMarker = this.CreateMarker(currentRow, 0, true, propDescriptor, currentGrid);
                    var rightMarker = this.CreateMarker(currentRow, 0, false, propDescriptor, currentGrid);

                    // apply IsReadOnly property to input control
                    this.ApplyReadOnly((FrameworkElement)item.Content, propDescriptor);

                    // add controls to the row collection
                    _rowCollection.Add(leftMarker, header, (UIElement)item.Content, rightMarker, propDescriptor);

                    if (!DesignerProperties.GetIsInDesignMode(this))
                    {
                        AnnotationReader.ChangeBindings((UIElement)item.Content, (dp, exp) =>
                            {
                                // cache the binding expressions
                                _bindingExpressions.Add((FrameworkElement)item.Content, exp);

                                // assign data source and change the binding to get notified on changes, validation, ...
                                BindingHelpers.ChangeBinding((FrameworkElement)item.Content, dp, (bind) =>
                                    {
                                        bind.Source = _source;
                                        bind.ValidatesOnDataErrors = true;
                                        bind.NotifyOnValidationError = true;
                                        bind.NotifyOnSourceUpdated = true;
                                        bind.NotifyOnTargetUpdated = true;
                                    });
                            });

                        // register focus handler
                        var focusItem = this.RegisterFocusHandler((UIElement)item.Content);
                        item.Content = focusItem;

                        // assign sub data source
                        if (typeof(Selector).IsAssignableFrom(item.Content.GetType()))
                        {
                            var source = this.SubDataSources[propDescriptor.AssociationName];
                            if (source != null && string.IsNullOrEmpty(source.DataSourcePropertyName) == false)
                            {
                                var binding = new Binding() { Source = this.DataContext, Path = new PropertyPath(source.DataSourcePropertyName) };
                                ((Selector)item.Content).SetBinding(Selector.ItemsSourceProperty, binding);
                            }
                        }
                    }

                    currentRow += 2;
                }
            }

            // create auto generated columns
            if (this.AutoGenerateColumns)
            {
                _typeDescriptor = AnnotationReader.GetTypeDescriptor(_dataObjectType);
                
                int rowCount = _typeDescriptor.PropertyDescriptors.Where(d=>d.IsVisible).ToList().Count;
                if (this.ColumnCount > rowCount)
                    this.ColumnCount = rowCount;
                int itemsPerColumn = (int)Math.Round((double)rowCount / this.ColumnCount, 0, MidpointRounding.AwayFromZero);
                Grid currentGrid = this.CreateColumnGrid(itemsPerColumn);
                for (int i = 0; i < this.ColumnCount; i++)
                {
                    _detailsGrid.ColumnDefinitions.Add(new ColumnDefinition { });
                    _detailsGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(20d, GridUnitType.Pixel) });
                }
                _detailsGrid.ColumnDefinitions.RemoveAt(_detailsGrid.ColumnDefinitions.Count - 1);
                int columnIndex = 0;
                int itemColumnCounter = 0;

                foreach (DataPropertyDescriptor propDescriptor in _typeDescriptor.PropertyDescriptors)
                {
                    if (propDescriptor.IsVisible)
                    {
                        if (itemColumnCounter == itemsPerColumn)
                        {
                            currentRow = 0;
                            columnIndex++;
                            currentGrid = this.CreateColumnGrid(itemsPerColumn);
                            currentGrid.SetValue(Grid.ColumnProperty, columnIndex+1);
                            itemColumnCounter = 0;
                        }
                        itemColumnCounter++;

                        // create header
                        var header = new ContentPresenter()
                        {
                            Content = new Label()
                            {
                                Padding = new Thickness(0),
                                Margin = new Thickness(0, 0, this.ColumnSpacing, 0),
                                HorizontalAlignment = HorizontalAlignment.Left,
                                VerticalAlignment = (typeof(ImageSource).IsAssignableFrom(propDescriptor.PropertyType)) ? VerticalAlignment.Top : VerticalAlignment.Center,
                                Content = propDescriptor.DisplayName + ":",
                            }
                        };
                        header.SetValue(Grid.RowProperty, currentRow);
                        header.SetValue(Grid.ColumnProperty, 0);
                        currentGrid.Children.Add(header);

                        // create binding
                        Binding bind = new Binding();
                        bind.Mode = BindingMode.TwoWay;
                        bind.Source = _source;
                        bind.Path = new PropertyPath(propDescriptor.PropertyName);
                        bind.ValidatesOnDataErrors = true;
                        bind.NotifyOnValidationError = true;
                        bind.NotifyOnSourceUpdated = true;
                        bind.NotifyOnTargetUpdated = true;
                        
                        // create content
                        var content = this.GetControl(propDescriptor, bind);
                        content.SetValue(Grid.RowProperty, currentRow);
                        content.SetValue(Grid.ColumnProperty, 2);
                        this.AssignEmptyErrorTemplate(content);
                        currentGrid.Children.Add(content);
                        propDescriptor.Control = content;

                        // get manually assigned annotations
                        var mapping = this.ControlMappings[propDescriptor.PropertyName];
                        if (mapping == null)
                            mapping = this.ControlMappings[propDescriptor.PropertyType];
                        if (mapping != null)
                        {
                            if (mapping.IsReadOnly != DefaultableBoolean.Default)
                                propDescriptor.IsReadOnly = (mapping.IsReadOnly == DefaultableBoolean.True) ? true : false;
                            if (mapping.IsRequired != DefaultableBoolean.Default)
                                propDescriptor.IsRequired = (mapping.IsReadOnly == DefaultableBoolean.True) ? true : false;
                            if (!string.IsNullOrEmpty(mapping.Description))
                                propDescriptor.Description = mapping.Description;
                        }
                        
                        // create ui annotation markers
                        var leftMarker = this.CreateMarker(currentRow, columnIndex, true, propDescriptor, currentGrid);
                        var rightMarker = this.CreateMarker(currentRow, columnIndex, false, propDescriptor, currentGrid);
                        
                        var focusItem = this.RegisterFocusHandler(content);
                        if (content is DatePicker)
                            content.Loaded += this.OnDatePickerLoaded;

                        // apply IsReadOnly property to input control
                        this.ApplyReadOnly((FrameworkElement)focusItem, propDescriptor);

                        // add controls to the row collection
                        _rowCollection.Add(leftMarker, header, focusItem, rightMarker, propDescriptor);

                        currentRow += 2;
                    }
                };
            }
            Validation.AddErrorHandler(this, this.OnErrorInternal);
        }

        private Grid CreateColumnGrid(int itemsPerColumn)
        {
            Grid grid = new Grid();
            grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
            grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
            grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
            grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(0d, GridUnitType.Auto) });
            for (int i = 0; i < itemsPerColumn; i++)
            {
                grid.RowDefinitions.Add(new RowDefinition() { Height = GridLength.Auto });
                grid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(this.RowSpacing) });
            }
            _detailsGrid.Children.Add(grid);
            //_detailsGrid.ShowGridLines = true;
            //grid.ShowGridLines = true;
            return grid;
        }
        
        #endregion
        
        #region GetControl

        private FrameworkElement GetControl(DataPropertyDescriptor propDescriptor, Binding bind)
        {
            FrameworkElement ctl = null;
            bind.Path = new PropertyPath(propDescriptor.PropertyName);

            if (this.ControlMappings != null && this.ControlMappings.Count > 0)
            {
                var mapping = this.ControlMappings[propDescriptor.PropertyType];
                if (mapping == null)
                {
                    mapping = this.ControlMappings[propDescriptor.PropertyName];
                    if (mapping != null)
                    {
                        if (!string.IsNullOrEmpty(mapping.DataPropertyName) &&
                            !mapping.DataPropertyName.Equals(propDescriptor.PropertyName))
                        {
                            goto l1;
                        }
                    }
                    else
                        goto l1;
                }

                if (mapping.ColumnTemplate != null && mapping.ColumnTemplate.HasContent)
                {
                    ctl = mapping.ColumnTemplate.LoadContent() as FrameworkElement;
                    if (ctl == null)
                        throw new InvalidOperationException(string.Format("Can't create column \"{0}\" from data template.", propDescriptor.PropertyName));
                    ctl.DataContext = _source;
                    var expressions = AnnotationReader.GetBindingExpressions(ctl);
                    if (expressions.Count > 0)
                        _bindingExpressions.Add(ctl, expressions[0]);
                    return ctl;
                }

                // create control
                var ctlType = mapping.ControlType;
                if (!typeof(FrameworkElement).IsAssignableFrom(ctlType))
                    throw new ArgumentException("Control types in ControlMappings collection must be of type FrameworkElement or derived types.");
                ctl = Activator.CreateInstance(mapping.ControlType) as FrameworkElement;
                if (ctl == null)
                    throw new ArgumentException(string.Format("Can't create control of type \"{0}\". Type isn't derived from FrameworkElement or no public constructor is available.", mapping.ControlType.ToString()), "ControlMapping.ControlType");
                ctl.HorizontalAlignment = HorizontalAlignment.Stretch;
                ctl.VerticalAlignment = VerticalAlignment.Stretch;

                // set binding
                var propName = mapping.BindingPropertyName;
                if (!propName.EndsWith("Property"))
                    propName += "Property";
                var info = mapping.ControlType.GetField(propName, System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.FlattenHierarchy);
                if (info == null)
                    throw new ArgumentException(string.Format("Can't find dependency property \"{0}\"!", mapping.BindingPropertyName), "ControlMapping.BindingPropertyName");
                var prop = info.GetValue(null) as DependencyProperty;
                if (prop == null)
                    throw new ArgumentException(string.Format("Can't read dependency property \"{0}\"!", mapping.BindingPropertyName), "ControlMapping.BindingPropertyName");
                if (mapping.Binding != null)
                {
                    ctl.SetBinding(prop, mapping.Binding);
                }
                else
                {
                    ctl.SetBinding(prop, bind);
                }
                var exp = ctl.GetBindingExpression(prop);
                _bindingExpressions.Add(ctl, exp);
                return ctl;
            }
        l1:
            // check for Nullable<T>
            var propertyType = propDescriptor.PropertyType;
            if (propDescriptor.PropertyType.IsGenericType && propDescriptor.PropertyType.Name == typeof(Nullable<>).Name)
            {
                propertyType = propDescriptor.PropertyType.GetGenericArguments()[0];
            }
            
            if (string.IsNullOrEmpty(propDescriptor.AssociationName) == false &&
                string.IsNullOrEmpty(propDescriptor.AssociationThisKey) == false &&
                string.IsNullOrEmpty(propDescriptor.AssociationOtherKey) == false)
            {
                ctl = new ComboBox()
                {
                    HorizontalAlignment = HorizontalAlignment.Stretch,
                    VerticalAlignment = VerticalAlignment.Center,
                    DisplayMemberPath = propDescriptor.AssociationOtherKey
                };
                ctl.SetBinding(ComboBox.SelectedItemProperty, bind);
                _bindingExpressions.Add(ctl, ctl.GetBindingExpression(ComboBox.SelectedItemProperty));
                var source = this.SubDataSources[propDescriptor.AssociationName];
                if (source != null && string.IsNullOrEmpty(source.DataSourcePropertyName) == false)
                {
                    var binding = new Binding() { Source = this.DataContext, Path = new PropertyPath(source.DataSourcePropertyName) };
                    ((ComboBox)ctl).SetBinding(ComboBox.ItemsSourceProperty, binding);
                }
            }
            else if (propertyType == typeof(string))
            {
                ctl = new TextBox()
                {
                    HorizontalAlignment = HorizontalAlignment.Stretch,
                    VerticalAlignment = VerticalAlignment.Center,
                };
                ctl.SetBinding(TextBox.TextProperty, bind);
                _bindingExpressions.Add(ctl, ctl.GetBindingExpression(TextBox.TextProperty));
            }
            else if (propertyType == typeof(int) ||
                propertyType == typeof(double) ||
                propertyType == typeof(decimal))
            {
                //ctl = new TextBox()
                //{
                //    HorizontalAlignment = HorizontalAlignment.Left,
                //    VerticalAlignment = VerticalAlignment.Center,
                //    MinWidth = 50,
                //};
                //ctl.SetBinding(TextBox.TextProperty, bind);
                ctl = new NumericUpDown()
                {
                    HorizontalAlignment = HorizontalAlignment.Left,
                    VerticalAlignment = VerticalAlignment.Center,
                    MinWidth = 50,
                    Minimum = 0,
                    Maximum = 1000000,
                    DisplayFormatString = propDescriptor.FormatString
                };
                ctl.SetBinding(NumericUpDown.ValueProperty, bind);
                _bindingExpressions.Add(ctl, ctl.GetBindingExpression(NumericUpDown.ValueProperty));
            }
            else if (propertyType == typeof(DateTime))
            {
                ctl = new DatePicker()
                {
                    HorizontalAlignment = HorizontalAlignment.Stretch,
                    VerticalAlignment = VerticalAlignment.Center,
                };
                ctl.SetBinding(DatePicker.SelectedDateProperty, bind);
                _bindingExpressions.Add(ctl, ctl.GetBindingExpression(DatePicker.SelectedDateProperty));
            }
            else if (propertyType == typeof(bool))
            {
                ctl = new CheckBox()
                {
                    HorizontalAlignment = HorizontalAlignment.Left,
                    VerticalAlignment = VerticalAlignment.Center,
                };
                ctl.SetBinding(CheckBox.IsCheckedProperty, bind);
                _bindingExpressions.Add(ctl, ctl.GetBindingExpression(CheckBox.IsCheckedProperty));
            }
            else if (typeof(ImageSource).IsAssignableFrom(propertyType))
            {
                var border = new Border
                {
                    HorizontalAlignment = HorizontalAlignment.Left,
                    VerticalAlignment = VerticalAlignment.Top,
                    BorderBrush = new SolidColorBrush(Colors.Gray),
                    BorderThickness = new Thickness(1),
                    CornerRadius= new CornerRadius(1)
                };
                ctl = new Image()
                {
                    HorizontalAlignment = HorizontalAlignment.Left,
                    VerticalAlignment = VerticalAlignment.Top,
                };
                ctl.SetBinding(Image.SourceProperty, bind);
                _bindingExpressions.Add(ctl, ctl.GetBindingExpression(Image.SourceProperty));
                var defaultImage = new Image
                {
                    HorizontalAlignment = HorizontalAlignment.Left,
                    VerticalAlignment = VerticalAlignment.Top,
                    Source = (this.DefaultImage != null) ? this.DefaultImage :  new BitmapImage(new Uri("/Thinktecture.DataForm;component/Images/DefaultImage.png", UriKind.Relative))
                };
                var nullBinding = new Binding
                {
                    Source = bind.Source,
                    Path = bind.Path,
                    Converter = new NullToVisibilityConverter()
                };
                defaultImage.SetBinding(Image.VisibilityProperty, nullBinding);
                var innerGrid = new Grid { };
                innerGrid.Children.Add(defaultImage);
                innerGrid.Children.Add(ctl);
                border.Child = innerGrid;
                var outerGrid = new Grid();
                outerGrid.ColumnDefinitions.Add(new ColumnDefinition());
                outerGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
                outerGrid.Children.Add(border);
                if (!propDescriptor.IsReadOnly)
                {
                    var bt = new Button
                    {
                        Content = this.GetLocalizedText(this.SelectionButtonsText), 
                        Margin = new Thickness(10, 0, 0, 0), 
                        Padding = new Thickness(5,0,5,0), 
                        HorizontalAlignment = HorizontalAlignment.Right, 
                        VerticalAlignment = VerticalAlignment.Top,
                        Tag = propDescriptor,
                        Command = this.SelectionButtonCommand
                    };
                    bt.Click += this.OnSelectionButtonClicked;
                    bt.SetValue(Grid.ColumnProperty, 1);
                    outerGrid.Children.Add(bt);
                }
                ctl = outerGrid;
            }
            else
            {
                ctl = new TextBlock()
                {
                    HorizontalAlignment = HorizontalAlignment.Left,
                    VerticalAlignment = VerticalAlignment.Center,
                    Text = propDescriptor.DisplayName + ":"
                };
                ctl.SetBinding(TextBlock.TextProperty, bind);
                _bindingExpressions.Add(ctl, ctl.GetBindingExpression(TextBlock.TextProperty));
            }

            // check readonly attribute
            if (propDescriptor.IsReadOnly)
            {
                var prop = ctl.GetType().GetProperty("IsReadOnly");
                if (prop != null)
                {
                    prop.SetValue(ctl, true, null);
                }
                prop = ctl.GetType().GetProperty("IsEnabled");
                if (prop != null)
                {
                    prop.SetValue(ctl, false, null);
                }
            }
            return ctl;
        }

        #endregion

        #region CreateMarker

        private Marker CreateMarker(int row, int col, bool left, Grid grid)
        {
            return this.CreateMarker(row, col, left, null, grid);
        }

        private Marker CreateMarker(int row, int col, bool left, DataPropertyDescriptor propDescriptor, Grid grid)
        {
            var marker = new Marker();
            marker.Style = this.MarkerStyle;
            marker.MarkerType = (left == false && propDescriptor != null && propDescriptor.IsRequired && this.ShowRequiredFieldMarker) ? MarkerType.RequiredField : MarkerType.None;
            marker.SetValue(Grid.RowProperty, row);
            marker.SetValue(Grid.ColumnProperty, left ? 1 : 3);
            marker.Margin = new Thickness(5, 0, 0, 0);
            if (grid != null)
                grid.Children.Add(marker);
            return marker;
        }

        #endregion

        #region ApplyReadOnly

        private void ApplyReadOnly(FrameworkElement element, DataPropertyDescriptor propertyDescriptor)
        {
            bool isReadOnly = this.IsReadOnly;
            if (!isReadOnly)
                isReadOnly = propertyDescriptor.IsReadOnly;
            var readOnlyProp = element.GetType().GetProperty("IsReadOnly", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
            if (readOnlyProp != null && !(typeof(ItemsControl).IsAssignableFrom(element.GetType())))
            {
                readOnlyProp.SetValue(element, isReadOnly, new object[] { });
            }
            else
            {
                ((FrameworkElement)element).IsEnabled = !isReadOnly;
            }
        }

        #endregion

        #region GetLocalizedText

        private string GetLocalizedText(string key)
        {
            var value = key;
            if (this.ResourceType != null && string.IsNullOrEmpty(key) == false)
            {
                var localizerProp = this.ResourceType.GetProperty(key, BindingFlags.NonPublic | BindingFlags.Static);
                if (localizerProp != null)
                {
                    var localizedValue = localizerProp.GetValue(null, new object[] { });
                    if (localizedValue != null)
                    {
                        value = localizedValue.ToString();
                    }
                }
            }
            return value;
        }

        #endregion

        #endregion

        #region Foucs Handling

        #region RegisterFocusHandler

        private UIElement RegisterFocusHandler(UIElement element)
        {
            FrameworkElement focusElement = element as FrameworkElement;
            if (element is DatePicker)
            {
                DatePicker picker = element as DatePicker;
                focusElement = picker.Template.FindName("PART_TextBox", picker) as FrameworkElement;
                if (focusElement != null)
                    focusElement = ((Control)focusElement).Template.FindName("PART_ContentHost", (FrameworkElement)focusElement) as FrameworkElement;
                else
                    focusElement = element as FrameworkElement;
            }
            else
            {
                focusElement = element as FrameworkElement;
            }
            if (focusElement != null)
            {
                focusElement.GotFocus += this.OnFieldGotFocus;
                focusElement.LostFocus += this.OnFieldLostFocus;
            }
            return focusElement;
        }

        #endregion

        #region UnregisterFocusHandler

        private void UnregisterFocusHandler()
        {
            if (_rowCollection == null) return;
            foreach (var row in _rowCollection)
            {
                if (row.Content != null)
                {
                    row.Content.GotFocus -= this.OnFieldGotFocus;
                    row.Content.LostFocus -= this.OnFieldLostFocus;
                }
            }
        }

        #endregion

        #region ResetEditMarkers

        private void ResetEditMarkers()
        {
            if (_rowCollection == null) return;
            foreach (var item in _rowCollection)
            {
                item.LeftMarker.MarkerType = MarkerType.None;
            }
        }

        #endregion

        #endregion

        #region Error Handling

        #region CheckForError

        private bool CheckForError(bool checkAddNew)
        {
            return this.CheckForError(checkAddNew, ValidationErrorEventAction.Added);
        }

        private bool CheckForError(bool checkAddNew, ValidationErrorEventAction action)
        {
            if (DesignerProperties.GetIsInDesignMode(this))
                return false;
            bool addNew = true;
            if (checkAddNew && _source != null && _source.IsAddingNew)
                addNew = this.HasError;
            bool hasError = false;
            foreach (var dictItem in _bindingExpressions)
            {
                var element = dictItem.Key;
                var exp = dictItem.Value;
                var error = _source.CurrentItem as IDataErrorInfo;
                if (addNew &&
                    error != null &&
                    exp.ParentBinding != null &&
                    exp.ParentBinding.Path != null &&
                    !string.IsNullOrEmpty(exp.ParentBinding.Path.Path) &&
                    !string.IsNullOrEmpty(error[exp.ParentBinding.Path.Path]))
                {
                    this.Error = error.Error;
                    this.SetErrorMarker(element, true, error[exp.ParentBinding.Path.Path], !hasError, action);
                    hasError = true;
                }
                else
                {
                    if (error != null &&
                        exp.ParentBinding != null &&
                        exp.ParentBinding.Path != null &&
                        !string.IsNullOrEmpty(exp.ParentBinding.Path.Path) &&
                        !string.IsNullOrEmpty(error[exp.ParentBinding.Path.Path]))
                    {
                        this.Error = error.Error;
                    }
                    this.SetErrorMarker(element, false, string.Empty, false, action);
                }
            }

            if (!string.IsNullOrEmpty(this.Error) && this.Error.EndsWith(Environment.NewLine))
                this.Error = this.Error.Substring(0, this.Error.Length - 2);

            this.HasError = hasError;
            if (!this.HasError && !string.IsNullOrEmpty(this.Error))
            {
                this.Error = string.Empty;
            }
            if (hasError && _source.IsAddingNew)
                _source.CommitNew();
            return hasError;
        }

        #endregion

        #region SetErrorMarker

        private void SetErrorMarker(FrameworkElement element, bool hasError, string errorText, bool firstElement, ValidationErrorEventAction action)
        {
            if (element == null ||
                _rowCollection == null ||
                _rowCollection[element] == null)
            {
                return;
            }
            if (hasError)
            {
                if (this.ShowErrorMarker)
                    _rowCollection[element].RightMarker.MarkerType = MarkerType.Error;
                if (firstElement)
                    element.Focus();
                if (action == ValidationErrorEventAction.Added)
                    this.OnValidationError(this.Error, _rowCollection[element].PropertyDescriptor.PropertyName);
            }
            else
            {
                _rowCollection[element].RightMarker.MarkerType = (_rowCollection[element].PropertyDescriptor.IsRequired && this.ShowRequiredFieldMarker) ? MarkerType.RequiredField : MarkerType.None;
            }
            if (!string.IsNullOrEmpty(errorText) && errorText.EndsWith(Environment.NewLine))
                errorText = errorText.Substring(0, errorText.Length - 2);
            _rowCollection[element].RightMarker.ToolTip = hasError ? errorText : "Required Field";
        }

        #endregion

        #region ResetErrors

        private void ResetErrors()
        {
            foreach (var dictItem in _bindingExpressions)
            {
                var element = dictItem.Key;
                if (element != null && _rowCollection[element] != null)
                    _rowCollection[element].RightMarker.MarkerType = (_rowCollection[element].PropertyDescriptor.IsRequired && this.ShowRequiredFieldMarker) ? MarkerType.RequiredField : MarkerType.None;
            }
            this.HasError = false;
            this.Error = string.Empty;
        }

        #endregion

        #region AssignEmptyErrorTemplate

        private void AssignEmptyErrorTemplate(FrameworkElement element)
        {
            if (element != null)
            {
                Uri resourceLocater =
                    new Uri("/Thinktecture.DataForm;component/Themes/generic.xaml", UriKind.Relative);
                ResourceDictionary dict = Application.LoadComponent(resourceLocater) as ResourceDictionary;
                object template = dict["EmptyErrorTemplate"];
                if (template != null && template is ControlTemplate)
                {
                    element.SetValue(Validation.ErrorTemplateProperty, template);
                }
            }
        }

        #endregion

        #endregion

        #region Command Handling

        #region RefreshCommands

        private void RefreshCommands()
        {
            this.MoveFirstCommand.CanExecute(null);
            this.MovePreviousCommand.CanExecute(null);
            this.MoveNextCommand.CanExecute(null);
            this.MoveLastCommand.CanExecute(null);
            this.MoveToPositionCommand.CanExecute(null);
            this.MoveToItemCommand.CanExecute(null);
            this.AddCommand.CanExecute(null);
            this.RemoveCommand.CanExecute(null);
        }
        
        #endregion

        #region CanMove

        private bool CanMove(Predicate<ListCollectionView> action)
        {
            return (_source != null && action(_source));
        }
        
        #endregion

        #region Move

        private void Move(Action<ListCollectionView> moveAction)
        {
            if (!this.CheckForError(false))
            {
                moveAction(_source);
                this.ResetEditMarkers();
                this.RefreshCommands();
            }
        }

        #endregion

        #endregion

        #endregion

        #region Public Functions

        #region DetachDataSource

        /// <summary>
        /// Detaches the data source from the control an releases all event handlers.
        /// </summary>
        public void DetachDataSource()
        {
            Validation.RemoveErrorHandler(this, this.OnErrorInternal);
            this.UnregisterChangeNotification();
            this.UnregisterFocusHandler();
            if (_source != null)
            {
                _source.CurrentChanging -= this.OnCurrentChanging;
                _source.CurrentChanged -= this.OnCurrentChanged;
            }
            _source = null;
            _typeDescriptor = null;
            _bindingExpressions.Clear();
            _rowCollection.Clear();
        }

        #endregion

        #region SetAssociationSource

        /// <summary>
        /// Sets the data source an association according to the given property name, e.g. a data source of a combobox.
        /// </summary>
        /// <param name="propertyName">Name of the property that is associated with the given data source.</param>
        /// <param name="source">An IEnumerable instance of the association data source.</param>
        public void SetAssociationSource(string propertyName, IEnumerable source)
        {
            var propDescriptor = _typeDescriptor.PropertyDescriptors[propertyName];
            if (propDescriptor != null)
            {
                var combo = propDescriptor.Control as ItemsControl;
                if (combo != null)
                {
                    combo.ItemsSource = source;
                }
            }
        }

        #endregion

        #region AcceptChanges

        /// <summary>
        /// Resets the edit markers.
        /// </summary>
        public void AcceptChanges()
        {
            foreach (var item in _rowCollection)
            {
                item.IsEdited = false;
                item.LeftMarker.MarkerType = MarkerType.None;
            }
            this.RefreshCommands();
        }

        #endregion

        #endregion

        #region Event Handling

        #region Control Events

        #region OnLoaded

        private void OnLoaded(object sender, RoutedEventArgs e)
        {
            this.Loaded -= this.OnLoaded;
            this.AssignDataSource();
            if (!_focusHandlerRegistered)
                this.OnLayoutUpdated(null, EventArgs.Empty);
            if (string.IsNullOrEmpty(this.Title) == false)
                this.Title = this.GetLocalizedText(this.Title);
            if (!string.IsNullOrEmpty(this.DefaultStatusText) && !string.IsNullOrEmpty(this.GetLocalizedText(this.DefaultStatusText)))
                this.CurrentStatusText = this.GetLocalizedText(this.DefaultStatusText);
            this.SelectionButtonsText = this.GetLocalizedText(this.SelectionButtonsText);
        }

        #endregion

        #region OnLayoutUpdated

        private void OnLayoutUpdated(object sender, EventArgs e)
        {
            this.LayoutUpdated -= this.OnLayoutUpdated;
            if (!DesignerProperties.GetIsInDesignMode(new DependencyObject()) &&
                _rowCollection != null &&
                _focusHandlerRegistered == false)
            {
                _focusHandlerRegistered = true;
                foreach (var item in _rowCollection)
                {
                    var focusItem = this.RegisterFocusHandler(item.Content);
                    item.Content = focusItem;
                }
            }
        }

        #endregion

        #endregion

        #region Data Events
        
        #region OnDataPropertyChanged

        private void OnDataPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (_rowCollection != null)
            {
                var dataRow = _rowCollection[e.PropertyName];
                if (dataRow != null)
                {
                    dataRow.IsEdited = true;
                    if (this.ShowEditTracker)
                        dataRow.LeftMarker.MarkerType = MarkerType.EditTracker;
                }
            }
            if (_source != null && _source.CurrentItem != null)
                this.OnItemChanged(_source.CurrentItem);
        }

        #endregion

        #region OnDataCollectionChanged

        private void OnDataCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.Action == NotifyCollectionChangedAction.Remove && e.OldItems != null)
            {
                foreach (var item in e.OldItems)
                {
                    // remove change handler
                    var notifyItem = item as INotifyPropertyChanged;
                    if (notifyItem != null)
                    {
                        notifyItem.PropertyChanged -= this.OnDataPropertyChanged;
                    }
                    // raise event
                    this.OnItemRemoved(item);
                }
            }
            else if (e.Action == NotifyCollectionChangedAction.Add && e.NewItems != null)
            {
                foreach (var item in e.NewItems)
                {
                    // add change handler
                    var notifyItem = item as INotifyPropertyChanged;
                    if (notifyItem != null)
                    {
                        notifyItem.PropertyChanged += this.OnDataPropertyChanged;
                    }
                    // raise event
                    this.OnItemAdded(item);
                }
                this.ExecuteMoveLastCommand(null);
            }
            // raise list change event
            this.OnCollectionChanged(e);
            this.RefreshCommands();
        }

        #endregion

        #endregion

        #region Focus Events
        
        #region OnFieldGotFocus

        private void OnFieldGotFocus(object sender, RoutedEventArgs e)
        {
            var element = sender as FrameworkElement;
            if (element != null && _rowCollection[element] != null)
            {
                if (this.ShowRowMarker)
                    _rowCollection[element].LeftMarker.MarkerType = MarkerType.FieldTracker;
                var description = _rowCollection[element].PropertyDescriptor.Description;
                this.CurrentStatusText = !string.IsNullOrEmpty(description) ? description : this.DefaultStatusText;
            }
        }

        #endregion

        #region OnFieldLostFocus

        private void OnFieldLostFocus(object sender, RoutedEventArgs e)
        {
            var element = sender as FrameworkElement;
            if (element != null && _rowCollection[element] != null)
            {
                if (_rowCollection[element].IsEdited && this.ShowEditTracker)
                    _rowCollection[element].LeftMarker.MarkerType = MarkerType.EditTracker;
                else
                    _rowCollection[element].LeftMarker.MarkerType = MarkerType.None;
            }
        }

        #endregion
        
        #region OnDatePickerLoaded

        private void OnDatePickerLoaded(object sender, RoutedEventArgs e)
        {
            var picker = sender as DatePicker;
            var focusElement = picker as FrameworkElement;
            picker.Loaded -= this.OnDatePickerLoaded;
            var element = picker.Template.FindName("PART_TextBox", picker) as FrameworkElement;
            if (element != null)
                focusElement = ((Control)element).Template.FindName("PART_ContentHost", (FrameworkElement)element) as FrameworkElement;
            else
                focusElement = element;
            if (focusElement != null && _rowCollection != null && _rowCollection[picker] != null)
            {
                focusElement.GotFocus += this.OnFieldGotFocus;
                focusElement.LostFocus += this.OnFieldLostFocus;
                _rowCollection[picker].Content = focusElement;
            }
        }
        
        #endregion

        #endregion

        #region Error Events

        #region OnErrorInternal

        private void OnErrorInternal(object sender, ValidationErrorEventArgs e)
        {
            if (!this.CheckForError(true, e.Action))
                e.Handled = true;
        }

        #endregion

        #endregion

        #region Command Handlers

        #region First

        private bool CanExecuteMoveFirstCommand(object parameter)
        {
            return this.CanMove(view => view.CurrentPosition > 0);
        }

        private void ExecuteMoveFirstCommand(object parameter)
        {
            this.Move(view => view.MoveCurrentToFirst());
        }

        #endregion

        #region Previous

        private bool CanExecuteMovePreviousCommand(object parameter)
        {
            return this.CanMove(view => view.CurrentPosition > 0);
        }

        private void ExecuteMovePreviousCommand(object parameter)
        {
            this.Move(view => view.MoveCurrentToPrevious());
        }

        #endregion

        #region Next

        private bool CanExecuteMoveNextCommand(object parameter)
        {
            return this.CanMove(view => view.CurrentPosition < view.Count - 1);
        }

        private void ExecuteMoveNextCommand(object parameter)
        {
            this.Move(view => view.MoveCurrentToNext());
        }

        #endregion

        #region Last

        private bool CanExecuteMoveLastCommand(object parameter)
        {
            return this.CanMove(view => view.CurrentPosition < view.Count - 1);
        }

        private void ExecuteMoveLastCommand(object parameter)
        {
            this.Move(view => view.MoveCurrentToLast());
        }

        #endregion

        #region MoveToPosition

        private bool CanExecuteMoveToPositionCommand(object parameter)
        {
            return this.CanMove(view => view.Count > 0);
        }

        private void ExecuteMoveToPositionCommand(object parameter)
        {
            if (parameter == null) return;
            int pos = -1;
            if (int.TryParse(parameter.ToString(), out pos))
            {
                this.Move(view => view.MoveCurrentToPosition(pos));
            }
        }

        #endregion

        #region MoveToItem

        private bool CanExecuteMoveToItemCommand(object parameter)
        {
            return this.CanMove(view => view.Count > 0);
        }

        private void ExecuteMoveToItemCommand(object parameter)
        {
            if (parameter == null) return;
            this.Move(view => view.MoveCurrentTo(parameter));
        }

        #endregion

        #region Add

        private bool CanExecuteAddCommand(object parameter)
        {
            return (_source != null);
        }

        private void ExecuteAddCommand(object parameter)
        {
            this.Move(view => view.AddNew());
        }

        #endregion

        #region Remove

        private bool CanExecuteRemoveCommand(object parameter)
        {
            return (_source != null && _source.CurrentItem != null);
        }

        private void ExecuteRemoveCommand(object parameter)
        {
            if (_source.IsAddingNew)
            {
                _source.CancelNew();
            }
            else if (_source.CurrentItem != null)
            {
                _source.CancelEdit();
                _source.Remove(_source.CurrentItem);
                if (_source.Count == 0)
                {
                    var item = _source.AddNew();
                }
            }
            this.ResetEditMarkers();
            this.RefreshCommands();
        }

        #endregion

        #region ActionCommand

        private bool CanExecuteActionCommand(object parameter)
        {
            return true;
        }

        private void ExecuteActionCommand(object parameter)
        {
            var actionParam = parameter as ActionParameter;
            if (actionParam != null)
            {
                var param = actionParam.Parameter;
                switch (actionParam.Action)
                {
                    case DataFormAction.Reset:
                        this.Generate();
                        break;
                    case DataFormAction.AcceptChanges:
                        this.AcceptChanges();
                        break;
                    case DataFormAction.DetachDataSource:
                        this.DetachDataSource();
                        break;
                    case DataFormAction.SetBusy:
                        this.IsBusy = true;
                        break;
                    case DataFormAction.SetAvailable:
                        this.IsBusy = false;
                        break;
                }
            }
        }

        #endregion
        
        #endregion

        #region SelectionButtons

        private void OnSelectionButtonClicked(object sender, RoutedEventArgs e)
        {
            var propDescriptor = ((Button)sender).Tag as DataPropertyDescriptor;
            this.OnSelectionButtonClicked(propDescriptor.PropertyName, _source.CurrentItem);
        }
        
        #endregion
        
        #endregion

        #region DataFormRow Class

        sealed class DataFormRow
        {
            public DataFormRow(Marker leftMarker, UIElement header, UIElement content, Marker rightMarker, DataPropertyDescriptor propertyDescriptor)
            {
                this.LeftMarker = leftMarker;
                this.Header = header;
                this.Content = content;
                this.RightMarker = rightMarker;
                this.PropertyDescriptor = propertyDescriptor;
            }
            public Marker LeftMarker { get; set; }
            public UIElement Header { get; set; }
            public UIElement Content { get; set; }
            public Marker RightMarker { get; set; }
            public DataPropertyDescriptor PropertyDescriptor { get; set; }
            public bool IsEdited { get; set; }
        }

        #endregion

        #region DataFormRowCollection Class

        sealed class DataFormRowCollection : List<DataFormRow>
        {
            public void Add(Marker leftMarker, UIElement header, UIElement content, Marker rightMarker, DataPropertyDescriptor propertyDescriptor)
            {
                this.Add(new DataFormRow(leftMarker, header, content, rightMarker, propertyDescriptor));
            }
            public DataFormRow this[UIElement contentElement]
            {
                get { return (from row in this where row.Content == contentElement select row).FirstOrDefault(); }
            }
            public DataFormRow this[Marker rightMarker]
            {
                get { return (from row in this where row.RightMarker == rightMarker select row).FirstOrDefault(); }
            }
            public DataFormRow this[string columnName]
            {
                get
                {
                    return (from row in this
                            where row.PropertyDescriptor != null && row.PropertyDescriptor.PropertyName == columnName
                            select row).FirstOrDefault();
                }
            }
        }

        #endregion

        #region ICollectionView Members

        public bool CanFilter
        {
            get { return (_source != null) ? _source.CanFilter: false; }
        }

        public bool CanGroup
        {
            get { return (_source != null) ? _source.CanGroup : false; }
        }

        public bool CanSort
        {
            get { return (_source != null) ? _source.CanSort : false; }
        }

        public bool Contains(object item)
        {
            return (_source != null) ? _source.Contains(item) : false;
        }

        public System.Globalization.CultureInfo Culture
        {
            get { return (_source != null) ? _source.Culture : null; }
            set { if (_source != null) _source.Culture = value; }
        }

        public event EventHandler CurrentChanged;
        protected virtual void OnCurrentChanged(object sender, EventArgs e)
        {
            if (CurrentChanged != null)
                CurrentChanged(this, e);
        }

        public event CurrentChangingEventHandler CurrentChanging;
        protected virtual void OnCurrentChanging(object sender, CurrentChangingEventArgs e)
        {
            if (CurrentChanging != null)
                CurrentChanging(this, e);
        }
        
        public object CurrentItem
        {
            get { return (_source != null) ? _source.CurrentItem : null; }
        }

        public int CurrentPosition
        {
            get { return (_source != null) ? _source.CurrentPosition : 0; }
        }

        public IDisposable DeferRefresh()
        {
            return (_source != null) ? _source.DeferRefresh() : null;
        }

        public Predicate<object> Filter
        {
            get { return (_source != null) ? _source.Filter : null; }
            set { if (_source != null) _source.Filter = value; }
        }

        public ObservableCollection<GroupDescription> GroupDescriptions
        {
            get { return (_source != null) ? _source.GroupDescriptions : null; }
        }

        public ReadOnlyObservableCollection<object> Groups
        {
            get { return (_source != null) ? _source.Groups : null; }
        }

        public bool IsCurrentAfterLast
        {
            get { return (_source != null) ? _source.IsCurrentAfterLast : true; }
        }

        public bool IsCurrentBeforeFirst
        {
            get { return (_source != null) ? _source.IsCurrentBeforeFirst : true; }
        }

        public bool IsEmpty
        {
            get { return (_source != null) ? _source.IsEmpty : true; }
        }

        public bool MoveCurrentTo(object item)
        {
            if (this.MoveToItemCommand.CanExecute(item))
            {
                this.MoveToItemCommand.Execute(item);
                return true;
            }
            return false;
        }

        public bool MoveCurrentToFirst()
        {
            if (this.MoveFirstCommand.CanExecute(null))
            {
                this.MoveFirstCommand.Execute(null);
                return true;
            }
            return false;
        }

        public bool MoveCurrentToLast()
        {
            if (this.MoveLastCommand.CanExecute(null))
            {
                this.MoveLastCommand.Execute(null);
                return true;
            }
            return false;
        }

        public bool MoveCurrentToNext()
        {
            if (this.MoveNextCommand.CanExecute(null))
            {
                this.MoveNextCommand.Execute(null);
                return true;
            }
            return false;
        }

        public bool MoveCurrentToPosition(int position)
        {
            if (this.MoveToPositionCommand.CanExecute(position))
            {
                this.MoveToPositionCommand.Execute(position);
                return true;
            }
            return false;
        }

        public bool MoveCurrentToPrevious()
        {
            if (this.MovePreviousCommand.CanExecute(null))
            {
                this.MovePreviousCommand.Execute(null);
                return true;
            }
            return false;
        }

        public void Refresh()
        {
            if (_source != null)
                _source.Refresh();
        }

        public SortDescriptionCollection SortDescriptions
        {
            get { return (_source != null) ? _source.SortDescriptions : null; }
        }

        public IEnumerable SourceCollection
        {
            get { return (_source != null) ? _source.SourceCollection : null; }
        }

        #endregion

        #region IEnumerable Members

        public IEnumerator GetEnumerator()
        {
            return (_source != null) ? ((IEnumerable)_source).GetEnumerator() : null;
        }

        #endregion

        #region INotifyCollectionChanged Members

        public event NotifyCollectionChangedEventHandler CollectionChanged;
        protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
        {
            if (CollectionChanged != null)
                CollectionChanged(this, e);
        }

        #endregion

    }

    #region DataFormValidationErrorEventHandler
    
    public delegate void DataFormValidationErrorEventHandler(object sender, DataFormValidationErrorEventArgs e);
    
    #endregion

    #region DataFormValidationErrorEventArgs
    
    public class DataFormValidationErrorEventArgs : RoutedEventArgs
    {
        public readonly string ErrorMessage;
        public readonly string PropertyName;
        public DataFormValidationErrorEventArgs(RoutedEvent routedEvent, object source, string errorMessage, string propertyName)
            : base(routedEvent, source)
        {
            this.ErrorMessage = errorMessage;
            this.PropertyName = propertyName;
        }
    }

    #endregion

    #region DataFormItemChangedEventHandler

    public delegate void DataFormItemChangedEventHandler(object sender, DataFormItemChangedEventArgs e);

    #endregion

    #region DataFormItemChangedEventArgs

    public class DataFormItemChangedEventArgs : RoutedEventArgs
    {
        public readonly object Item;
        public DataFormItemChangedEventArgs(RoutedEvent routedEvent, object source, object item)
            : base(routedEvent, source)
        {
            this.Item = item;
        }
    }

    #endregion

    #region DataFormSelectionButtonClickedEventHandler

    public delegate void DataFormSelectionButtonClickedEventHandler(object sender, DataFormSelectionButtonClickedEventArgs e);

    #endregion

    #region DataFormSelectionButtonClickedEventArgs

    public class DataFormSelectionButtonClickedEventArgs : RoutedEventArgs
    {
        public readonly string PropertyName;
        public readonly object Item;
        public DataFormSelectionButtonClickedEventArgs(RoutedEvent routedEvent, object source, string propertyName, object item)
            : base(routedEvent, source)
        {
            this.PropertyName = propertyName;
            this.Item = item;
        }
    }

    #endregion

    #region DataFormAction
    
    /// <summary>
    /// Defines actions that can be performed via the ActionCommand of the DataForm.
    /// </summary>
    public enum DataFormAction
    {
        /// <summary>
        /// Resets the bound list.
        /// </summary>
        Reset,
        /// <summary>
        /// Resets the edit markers of the DataForm.
        /// </summary>
        AcceptChanges,
        /// <summary>
        /// Detaches the bound list and all event handlers.
        /// </summary>
        DetachDataSource,
        /// <summary>
        /// Displays the BusyIndicator to signal busy state.
        /// </summary>
        SetBusy,
        /// <summary>
        /// Hides the BusyIndicator.
        /// </summary>
        SetAvailable
    }
    
    #endregion

}
